import Watcher from './watcher.js'

const DirectiveHandler = {
  text (node, val, key) {
    node.textContent = val
  },
  model (node, val, key) {
    node.value = val
    node.addEventListener('input', () => {
      this.vm[key] = node.value
    })
  },
  html (node, val, key) {
    node.innerHTML = val
  },
  on (node, callback, key, eventName) {
    // 为 callback 绑定 this.vm 作为上下文
    node.addEventListener(eventName, callback.bind(this.vm))
  }
}

export default class Compiler {
  constructor (vm) {
    this.vm = vm
    this.el = vm.$el
    this.compile(this.el)
  }
  // 编译模板, 处理文本节点和元素节点
  compile (el) {
    // childNodes 包含文本节点, 而 children 不含
    Array.from(el.childNodes).forEach(node => {
      if (this.isTextNode(node)) {
        this.compileText(node)
      } else if (this.isElementNode(node)) {
        this.compileElement(node)
      }
      if (node.childNodes && node.childNodes.length) {
        this.compile(node)
      }
    })
  }
  // 编译元素节点, 处理指令 v-text 和 v-html
  compileElement (node) {
    Array.from(node.attributes).filter(this.isDirective).forEach(attr => {
      // v-on:click -> [on, click]
      const [name, eventName] = attr.name.slice(2).split(':')
      const key = attr.value
      this.updateElement(node, name, key, this.vm[key], eventName)
      new Watcher(this.vm, key, val => {
        this.updateElement(node, name, key, val)
      })
    })
  }
  updateElement (node, name, key, val, eventName) {
    if (name === 'on' && eventName) {
      val = this.vm.$methods[key]
    }
    const handle = DirectiveHandler[name]
    handle && handle.call(this, node, val, key, eventName)
  }
  // 编译文本节点, 处理插值表达式
  compileText (node) {
    const template = node.textContent
    this.updateText(node, template)
  }
  updateText (node, template, key, val) {
    node.textContent = template.replace(/\{\{(.+?)\}\}/g, (match, $1) => {
      $1 = $1.trim()
      if ($1 === key) return val
      new Watcher(this.vm, $1, val => {
        this.updateText(node, template, $1, val)
      })
      return this.vm[$1]
    })
  }
  // 判断属性是否为指令
  isDirective (attr) {
    return attr.name.startsWith('v-')
  }
  // 判断是否为文本节点
  isTextNode (node) {
    return node.nodeType === 3
  }
  // 判断是否为元素节点
  isElementNode (node) {
    return node.nodeType === 1
  }
}
